package com.ikingtech.framework.sdk.web.configuration;

import com.ikingtech.framework.sdk.enums.common.FrameworkArchitectureTypeEnum;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.web.annotation.RequestOrder;
import com.ikingtech.framework.sdk.web.properties.WebProperties;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.parameters.HeaderParameter;
import io.swagger.v3.oas.models.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.GroupedOpenApi;
import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
import org.springdoc.core.customizers.GlobalOperationCustomizer;
import org.springdoc.core.filters.GlobalOpenApiMethodFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;

import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author tie yan
 */
@RequiredArgsConstructor
public class OpenApiConfiguration {

    @Value("${spring.application.name}")
    private String applicationName;

    @Value("${iking.framework.web.version}")
    private String version;

    private final WebProperties properties;

    public static final String EXTENSION_X_ORDER = "x-order";

    @Bean
    public GlobalOpenApiCustomizer orderGlobalOpenApiCustomizer() {
        return openApi -> {
            Paths paths = new Paths();
            openApi.getPaths().forEach((path, item) -> {
                List<io.swagger.v3.oas.models.Operation> operations = item.readOperations();
                if (Tools.Coll.isNotBlank(operations)) {
                    if (FrameworkArchitectureTypeEnum.MICRO_SERVICE.equals(this.properties.getArch())) {
                        paths.put("/" + applicationName + path, item);
                    } else {
                        paths.put(path, item);
                    }
                }
            });
            Paths sortedPath = new Paths();
            paths.entrySet().stream()
                    .sorted((current, next) -> {
                        int currentValue = (int) current.getValue().readOperations().get(0).getExtensions().getOrDefault(EXTENSION_X_ORDER, 999);
                        int nextValue = (int) next.getValue().readOperations().get(0).getExtensions().getOrDefault(EXTENSION_X_ORDER, 999);
                        return Integer.compare(currentValue, nextValue);
                    })
                    .forEach(entry -> sortedPath.put(entry.getKey(), entry.getValue()));

            openApi.setPaths(sortedPath);
            openApi.setTags(openApi.getTags().stream().sorted(Comparator.comparing(Tag::getName)).collect(Collectors.toList()));
        };
    }

    @Bean
    public GlobalOpenApiMethodFilter openApiMethodFilter() {
        return handlerMethod -> {
            Operation operationAnnotation = handlerMethod.getAnnotation(Operation.class);
            return null == operationAnnotation;
        };
    }

    @Bean
    public GlobalOperationCustomizer operationCustomize() {
        return (operation, handlerMethod) -> {
            RequestOrder requestOrderAnnotation = handlerMethod.getMethodAnnotation(RequestOrder.class);
            if (null == requestOrderAnnotation) {
                return null;
            }
            operation.addParametersItem(
                    new HeaderParameter().name("Authorization").example("Bearer iking").description("身份认证Token").required(true)
            );
            operation.addExtension(EXTENSION_X_ORDER, requestOrderAnnotation.order());
            return operation;
        };
    }

    @Bean
    public GroupedOpenApi groupedOpenApi() {
        String[] paths = {"/**"};
        return GroupedOpenApi.builder()
                .group(this.applicationName)
                .pathsToMatch(paths)
                .build();
    }

    @Bean
    public OpenAPI customOpenApi() {
        return new OpenAPI()
                .info(new Info()
                        .title(this.applicationName + " 接口文档")
                        .version(this.version)
                );
    }
}
